package com.sunday.common.core.asserts;

import com.sunday.common.core.constants.LogConstants;
import com.sunday.common.core.enums.ErrorCodeEnum;
import com.sunday.common.core.exception.BusinessException;
import com.sunday.common.core.remoting.base.response.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;

import java.util.Map;
import java.util.function.Supplier;

/**
 * 业务断言
 *
 * @author zsy
 * @since 2022/10/14
 */
@Slf4j
public class BusinessAssert {

    public static void isSuccess(Response response) {
        isSuccess(response, null, null);
    }

    public static void isSuccess(Response response, @Nullable ErrorCodeEnum errorCodeEnum) {
        isSuccess(response, errorCodeEnum, null);
    }

    public static void isSuccess(Response response, @Nullable Supplier<String> messageSupplier) {
        isSuccess(response, null, messageSupplier, false);
    }

    public static void isSuccess(Response response, @Nullable ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier) {
        isSuccess(response, errorCodeEnum, messageSupplier, false);
    }

    /**
     * 业务是否成功响应
     *
     * @param response        响应结果，根据 success 布尔结果确定执行流程
     * @param errorCodeEnum   替换的错误码信息
     * @param messageSupplier 自定义错误话术
     * @param isAppend        是否拼接错误，不拼接则替换 errorCodeEnum中的errorMessage
     */
    public static void isSuccess(Response response, @Nullable ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier, boolean isAppend) {
        assert response != null : "response must not be null";
        if (!response.isSuccess()) {
            if (errorCodeEnum == null) {
                throw new BusinessException(response.getErrorCode(), response.getErrorMessage(), nullSafeGet(messageSupplier), isAppend);
            } else {
                throw new BusinessException(errorCodeEnum, nullSafeGet(messageSupplier), isAppend);
            }
        }
    }

    public static void isTrue(boolean expression, ErrorCodeEnum errorCodeEnum) {
        isTrue(expression, errorCodeEnum, null);
    }

    public static void isTrue(boolean expression, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier) {
        isTrue(expression, errorCodeEnum, messageSupplier, false);
    }

    public static void isTrue(boolean expression, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier, boolean isAppend) {
        assert errorCodeEnum != null : "errorCodeEnum must not be null";
        if (!expression) {
            throw new BusinessException(errorCodeEnum, nullSafeGet(messageSupplier), isAppend);
        }
    }

    public static void containsKey(Map<?, ?> map, Object key, ErrorCodeEnum errorCodeEnum) {
        containsKey(map, key, errorCodeEnum, null);
    }

    public static void containsKey(Map<?, ?> map, Object key, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier) {
        containsKey(map, key, errorCodeEnum, messageSupplier, false);
    }

    public static void containsKey(Map<?, ?> map, Object key, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier, boolean isAppend) {
        assert errorCodeEnum != null : "errorCodeEnum must not be null";
        if (ObjectUtils.isEmpty(map) || !map.containsKey(key)) {
            throw new BusinessException(errorCodeEnum, nullSafeGet(messageSupplier), isAppend);
        }
    }

    public static void hasLength(@Nullable String text, int max, ErrorCodeEnum errorCodeEnum) {
        hasLength(text, max, errorCodeEnum, null);
    }

    public static void hasLength(@Nullable String text, int max, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier) {
        hasLength(text, max, errorCodeEnum, messageSupplier, false);
    }

    public static void hasLength(@Nullable String text, int max, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier, boolean isAppend) {
        assert errorCodeEnum != null : "errorCodeEnum must not be null";
        if (StringUtils.isBlank(text) || text.length() > max) {
            throw new BusinessException(errorCodeEnum, nullSafeGet(messageSupplier), isAppend);
        }
    }

    public static void hasText(@Nullable String text, ErrorCodeEnum errorCodeEnum) {
        hasText(text, errorCodeEnum, null);
    }

    public static void hasText(@Nullable String text, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier) {
        hasText(text, errorCodeEnum, messageSupplier, false);
    }

    public static void hasText(@Nullable String text, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier, boolean isAppend) {
        assert errorCodeEnum != null : "errorCodeEnum must not be null";
        if (StringUtils.isBlank(text)) {
            throw new BusinessException(errorCodeEnum, nullSafeGet(messageSupplier), isAppend);
        }
    }

    public static void notNull(@Nullable Object obj, ErrorCodeEnum errorCodeEnum) {
        notNull(obj, errorCodeEnum, null);
    }

    public static void notNull(@Nullable Object obj, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier) {
        notNull(obj, errorCodeEnum, messageSupplier, false);
    }

    public static void notNull(@Nullable Object obj, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier, boolean isAppend) {
        assert errorCodeEnum != null : "errorCodeEnum must not be null";
        if (ObjectUtils.isEmpty(obj)) {
            throw new BusinessException(errorCodeEnum, nullSafeGet(messageSupplier), isAppend);
        }
    }

    public static <T> T maybeGet(CallbackHandler<T> handler, ErrorCodeEnum errorCodeEnum) {
        return maybeGet(handler, errorCodeEnum, null);
    }

    public static <T> T maybeGet(CallbackHandler<T> handler, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier) {
        return maybeGet(handler, errorCodeEnum, messageSupplier, false);
    }

    public static <T> T maybeGet(CallbackHandler<T> handler, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier, boolean isAppend) {
        assert handler != null : "handler must not be null";
        assert errorCodeEnum != null : "errorCodeEnum must not be null";
        try {
            return handler.get();
        } catch (Throwable e) {
            log.error(LogConstants.error_format, e.toString(), e);
            throw new BusinessException(errorCodeEnum, nullSafeGet(messageSupplier), isAppend);
        }
    }

    public static void maybeException(ExecuteHandler handler, ErrorCodeEnum errorCodeEnum) {
        maybeException(handler, errorCodeEnum, null);
    }

    public static void maybeException(ExecuteHandler handler, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier) {
        maybeException(handler, errorCodeEnum, messageSupplier, false);
    }

    public static void maybeException(ExecuteHandler handler, ErrorCodeEnum errorCodeEnum, @Nullable Supplier<String> messageSupplier, boolean isAppend) {
        assert handler != null : "handler must not be null";
        assert errorCodeEnum != null : "errorCodeEnum must not be null";
        try {
            handler.doWith();
        } catch (Throwable e) {
            log.error(LogConstants.error_format, e.toString(), e);
            throw new BusinessException(errorCodeEnum, nullSafeGet(messageSupplier), isAppend);
        }
    }

    /**
     * 表达式，为true继续进行后续判定
     *
     * @param expression 表达式
     * @param handler
     * @see org.springframework.util.Assert#state(boolean, java.util.function.Supplier)
     */
    public static void state(boolean expression, AccessHandler handler) {
        assert handler != null : "handler must not be null";
        if (expression) {
            handler.validation();
        }
    }

    @Nullable
    private static String nullSafeGet(@Nullable Supplier<String> messageSupplier) {
        return (messageSupplier != null ? messageSupplier.get() : null);
    }

    /**
     * 访问处理程序
     */
    @FunctionalInterface
    public interface AccessHandler {
        void validation();
    }

    /**
     * 执行处理程序
     */
    @FunctionalInterface
    public interface ExecuteHandler {
        void doWith() throws Exception;
    }

    /**
     * 回调处理程序
     */
    @FunctionalInterface
    public interface CallbackHandler<T> {
        T get() throws Exception;
    }

}
